
// #include "../../overset/tioga/box.hpp"
#define NDEBUG
#include "../../extream/Common/include/array1d.hpp"
#include <cassert>
#include <iostream>
#include <vector>
using namespace std;
using namespace EXTREAM;
#include "array3d.hpp"

template <typename T, typename A = std::allocator<T>>
class default_init_allocator : public A {
  typedef std::allocator_traits<A> a_t;

public:
  template <typename U> struct rebind {
    using other =
        default_init_allocator<U, typename a_t::template rebind_alloc<U>>;
  };

  using A::A;

  template <typename U>
  void
  construct(U *ptr) noexcept(std::is_nothrow_default_constructible<U>::value) {
    ::new (static_cast<void *>(ptr)) U;
  }
  template <typename U, typename... Args>
  void construct(U *ptr, Args &&... args) {
    a_t::construct(static_cast<A &>(*this), ptr, std::forward<Args>(args)...);
  }
};

struct Box {
  double p[6];
  // real_t dxyz_[3];

  //
  Box(){};

  Box(const double lower[3], const double upper[3]) {
    p[0] = lower[0];
    p[1] = lower[1];
    p[2] = lower[2];
    p[3] = upper[0];
    p[4] = upper[1];
    p[5] = upper[2];

    // dxyz_[0] = p[3] - p[0];
    // dxyz_[1] = p[4] - p[1];
    // dxyz_[2] = p[5] - p[2];
  }
  double &operator[](int i) {
    assert(-1 < i && i < 6);
    return p[i];
  }
};

#include <chrono>
#include <cstdio>
void MyTimer(int type, const char *info = nullptr) {
  static std::chrono::steady_clock::time_point start;
  if (type == 0) {
    start = std::chrono::steady_clock::now();
    printf("Begin %s\n", info);
    return;
  } else {
    auto end = std::chrono::steady_clock::now();
    auto dt =
        std::chrono::duration_cast<std::chrono::duration<double>>(end - start);
    printf("End %s, time taken=%lf\n", info, dt.count());
  }
}

int main() {
  const int n = 1000000;

  Array3d<Box> arr3d;
  MyTimer(0, "start from array3d");
  arr3d.init(100, 100, 1000);
  for (int i = 0; i < n; ++i)
    arr3d[i][0] = i;
  MyTimer(1, "array3d end");

  Box *p;
  MyTimer(0, "start from pointer");
  p = new Box[n];
  for (int i = 0; i < n; ++i)
    p[i][0] = i;
  MyTimer(1, "end");
  delete[] p;

  vector<Box> bx;
  MyTimer(0, "start from vector");
  bx.resize(n);
  for (int i = 0; i < n; ++i)
    bx[i][0] = i;
  MyTimer(1, "end");

  Array3d<double> rarr;
  MyTimer(0, "start from array3d double");
  rarr.init(100, 100, 1000);
  MyTimer(1, "array3d end");
  rarr[1000] = 1000;

  double *rp;
  MyTimer(0, "start from pointer double");
  rp = new double[n];
  MyTimer(1, "pointer end");
  rp[1000] = 10000;

  vector<double, default_init_allocator<double>> rv;
  MyTimer(0, "start from vector  double");
  rv.resize(n);
  MyTimer(1, "vector end");
  rv[1000] = 10000;
  cout << rv[10000] << endl;

  MyTimer(0, "start from vector2");
  vector<double> rv2(n);
  MyTimer(1, "vector end2");

  MyTimer(0, "start from array1d");
  Array1d<double> arr1d(n);
  MyTimer(1, "");

  MyTimer(0, "start from array1d");
  Array1d<Box> arr1dbox(n);
  MyTimer(1, "");
  double low[3]{10, 10, 20}, hi[3]{30, 40, 50};
  arr1dbox[100] = Box(low, hi);

  MyTimer(0, "start from vector");
  vector<Box> vecbox(n);
  MyTimer(1, "");
  vecbox[100] = Box(low, hi);

  vector<Box> vecbox2;
  MyTimer(0, "start from vector");
  vecbox2.resize(n);
  MyTimer(1, "");
  vecbox2[100] = Box(low, hi);

  MyTimer(0, "start from pointer");
  Box *pb = new Box[n];
  MyTimer(1, "");
  pb[100] = Box(low, hi);
  delete[] pb;
  delete[] rp;

  return 0;
}